use crate::ecc_const::{SM2_N, SM2_N_K0, SM2_P, SM2_P_K0};
use crate::mont::{Mont, MontSM2p, Montgomery};
use crate::point::Point;
use crate::U256;
use std::fmt;
use std::mem::MaybeUninit;

const W: u64 = 5;
#[allow(non_upper_case_globals)]
const wSize: usize = 1 << (W - 1);
#[allow(non_upper_case_globals)]
const BaseW: u64 = 6;
#[allow(non_upper_case_globals)]
const wBaseSize: usize = 1 << (BaseW - 1);
#[allow(non_upper_case_globals)]
const maxBaseNAF: usize = (64 * 4 - 1 + BaseW as usize) / BaseW as usize;

type GwNAF = [(U256, U256); wBaseSize];

/// trait Curve abstract for elliptic curve
///     Jacobian coordinates satisfying
///         x = X/Z^2
///         y = Y/Z^3
///     Jacobian affined point(x, y, z) in forms of montgomery
pub trait Curve {
    fn double_jacob(&self, p: &Point) -> Point;
    // doublez_jacob for z is mont_one
    fn doublez_jacob(&self, x: &U256, y: &U256) -> Point {
        let p = Point::new(x, y, self.mont_one());
        self.double_jacob(&p)
    }
    fn add_jacob(&self, p: &Point, q: &Point) -> Point;
    fn addz_jacob(&self, p: &Point, qx: &U256, qy: &U256) -> Point {
        let q = Point::new(qx, qy, self.mont_one());
        self.add_jacob(p, &q)
    }
    fn mont_one(&self) -> &U256; // montgomery one GF(p)
    fn to_montgomery(&self, x: &U256) -> U256;
    fn from_montgomery(&self, y: &U256) -> U256;
    fn point_on_curve(&self, p: &Point) -> bool;
    // return point(x, y) from jacobian affined point(x, y, z)
    // in forms of Montgomery
    fn apply_z_mont(&self, p: &Point) -> (U256, U256);
    // return point(x, y) in GF(p)
    #[inline]
    fn apply_z(&self, p: &Point) -> (U256, U256) {
        let (xp, yp) = self.apply_z_mont(&p);
        (self.from_montgomery(&xp), self.from_montgomery(&yp))
    }
    // convert to jacobian affined point(x, y, z)
    #[inline]
    fn to_affined(&self, x: &U256, y: &U256) -> Point {
        let xp = self.to_montgomery(x);
        let yp = self.to_montgomery(y);
        Point {
            x: xp,
            y: yp,
            z: self.mont_one().clone(),
        }
    }
    #[inline]
    fn is_on_curve(&self, x: &U256, y: &U256) -> bool {
        let p = self.to_affined(x, y);
        self.point_on_curve(&p)
    }
    #[inline]
    fn point_double(&self, x: &U256, y: &U256) -> (U256, U256) {
        let xp = self.to_montgomery(x);
        let yp = self.to_montgomery(y);
        let q = self.doublez_jacob(&xp, &yp);
        self.apply_z(&q)
    }
    #[inline]
    fn point_add(&self, x1: &U256, y1: &U256, x2: &U256, y2: &U256) -> (U256, U256) {
        let p = self.to_affined(x1, y1);
        let xp = self.to_montgomery(x2);
        let yp = self.to_montgomery(y2);
        let q = self.addz_jacob(&p, &xp, &yp);
        self.apply_z(&q)
    }
    fn point_eq(&self, p: &Point, q: &Point) -> bool {
        if p == q {
            return true;
        }
        if *p.z() == *self.mont_one() {
            let (qx, qy) = self.apply_z_mont(q);
            *p.x() == qx && *p.y() == qy
        } else {
            let (px, py) = self.apply_z_mont(p);
            let (qx, qy) = self.apply_z_mont(q);
            (px, py) == (qx, qy)
        }
    }
}

/// trait CurveSW abstract for short Weierstrass curves
///     coordinates x y satisfying
///         y^2 = x^3 + a * x + b
///
/// default impl for short Weierstrass curves
///     K0 is K0 of prime P, default for SM2/NIST-P256
pub trait CurveSW<const K0: u64, const NK0: u64, const A_IS_N3: bool = true> {
    fn p(&self) -> &U256; // p prime, the order of the underlying field
    fn n(&self) -> &U256; // n prime, the order of the base point
    fn a(&self) -> &U256; // a, the constant of the curve equation
    fn b(&self) -> &U256; // b, the constant of the curve equation
    fn gx(&self) -> &U256; // x coordinate of the base point
    fn gy(&self) -> &U256; // y coordinate of the base point
    fn name(&self) -> &str; // the canonical name of the curve
    fn p_mont(&self) -> &dyn Montgomery<K0>;
    fn n_mont(&self) -> &dyn Montgomery<NK0>;
    fn mont_a(&self) -> &U256;
    fn mont_one(&self) -> &U256;
    fn select_base_point(&self, digit: u32, lvl: u64, sign: u64) -> (U256, U256);
    #[allow(non_snake_case)]
    fn select_base_NAF(&self, lvl: u64) -> &GwNAF;
    fn p_minus_n(&self) -> U256 {
        let (res, _) = self.p().sub(self.n());
        res
    }
    // return point(x, y) in GF(p)
    #[inline]
    fn apply_z(&self, p: &Point) -> (U256, U256) {
        let (xp, yp) = self.apply_z_mont(&p);
        (self.from_montgomery(&xp), self.from_montgomery(&yp))
    }
    // convert to jacobian affined point(x, y, z)
    #[inline]
    fn to_affined(&self, x: &U256, y: &U256) -> Point {
        let xp = self.to_montgomery(x);
        let yp = self.to_montgomery(y);
        Point {
            x: xp,
            y: yp,
            z: self.mont_one().clone(),
        }
    }
    #[inline]
    fn is_on_curve(&self, x: &U256, y: &U256) -> bool {
        let p = self.to_affined(x, y);
        self.point_on_curve(&p)
    }
    #[inline]
    fn point_double(&self, x: &U256, y: &U256) -> (U256, U256) {
        let xp = self.to_montgomery(x);
        let yp = self.to_montgomery(y);
        let q = self.doublez_jacob(&xp, &yp);
        self.apply_z(&q)
    }
    #[inline]
    fn point_add(&self, x1: &U256, y1: &U256, x2: &U256, y2: &U256) -> (U256, U256) {
        let p = self.to_affined(x1, y1);
        let xp = self.to_montgomery(x2);
        let yp = self.to_montgomery(y2);
        let q = self.addz_jacob(&p, &xp, &yp);
        self.apply_z(&q)
    }
    // eq compare for point jacob affined
    fn point_eq(&self, p: &Point, q: &Point) -> bool {
        if p == q {
            return true;
        }
        if *p.z() == *self.mont_one() {
            let (qx, qy) = self.apply_z_mont(q);
            *p.x() == qx && *p.y() == qy
        } else {
            let (px, py) = self.apply_z_mont(p);
            let (qx, qy) = self.apply_z_mont(q);
            (px, py) == (qx, qy)
        }
    }
    // only for A_IS_N3 false
    #[inline]
    fn a_is_zero(&self) -> bool {
        if A_IS_N3 {
            false
        } else {
            self.a().is_zero()
        }
    }
    fn mod_add(&self, a: &U256, b: &U256) -> U256 {
        a.mod_add(b, self.p())
    }
    fn mod_add_to(&self, a: &mut U256, b: &U256) {
        a.mod_add_to(b, self.p())
    }
    fn mod_sub(&self, a: &U256, b: &U256) -> U256 {
        a.mod_sub(b, self.p())
    }
    fn mod_sub_from(&self, a: &mut U256, b: &U256) {
        a.mod_sub_from(b, self.p())
    }
    // mmult -- p_mont mult
    fn mmult(&self, x: &U256, y: &U256) -> U256 {
        self.p_mont().mult(x, y)
    }
    // msqr -- pmont sqr
    fn msqr(&self, x: &U256, n: u32) -> U256 {
        self.p_mont().sqr(x, n)
    }
    #[inline]
    fn prod_equal(&self, prod: &U256, x: &U256, yp: &U256) -> bool {
        let xp = self.to_montgomery(x);
        let xp = self.mmult(&xp, yp);
        xp == *prod
    }
    #[inline]
    fn mont_mult2(&self, x: &U256) -> U256 {
        let mut res = x.clone();
        let cc = res.lshift1();
        res.modp(self.p(), cc)
    }
    fn n_mult(&self, x: &U256, y: &U256) -> U256 {
        self.n_mont().mult(x, y)
    }
    // dbl-1998-cmo-2 algorithm
    // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html
    // cost: 3M + 6S + 1*a + 4add + 2*2 + 1*3 + 1*4 + 1*8
    // M ... l1
    // S ... l2
    // T ... x3
    fn double_jacob(&self, p: &Point) -> Point {
        let z_is_one = *p.z() == *self.mont_one();
        let mut t1: U256;
        let mut t2: U256;
        let mut l1: U256;
        let mut l2: U256;
        if A_IS_N3 {
            /* Use the faster case.  */
            /* L1 = 3(X - Z^2)(X + Z^2) */
            /* square is faster than multiple	*/
            // L1 = 3(X^2 - Z^4)
            /*						T1: used for Z^2. */
            /*						T2: used for the right term. */
            if z_is_one {
                // l1 = X^2
                l1 = self.msqr(p.x(), 1);
                // l1 = x^2 - z^4
                self.mod_sub_from(&mut l1, self.mont_one());
                // 3(X^2 - Z^4) = 2(X^2 - Z^4) + (X^2 - Z^4)
                // t1 = 2 * l1
                t1 = self.mont_mult2(&l1);
                self.mod_add_to(&mut l1, &t1);
            } else {
                // t1 = Z^2
                t1 = self.msqr(p.z(), 1);
                // l1 = X - Z^2
                l1 = self.mod_sub(p.x(), &t1);
                // 3(X - Z^2) = 2(X - Z^2) + (X - Z^2)
                // t2 = 2 * l1
                t2 = self.mont_mult2(&l1);
                // l1 = l1 + 2 * l1 = 3(X - Z^2)
                self.mod_add_to(&mut l1, &t2);
                // t2 = X + Z^2
                t2 = self.mod_add(p.x(), &t1);
                // l1 = 3(X - Z^2)(X + Z^2)
                l1 = self.mmult(&l1, &t2);
            }
        } else {
            /* Standard case. */
            /* L1 = 3X^2 + aZ^4 */
            /*					T1: used for aZ^4. */
            // l1 = X^2
            l1 = self.msqr(p.x(), 1);
            t1 = self.mont_mult2(&l1);
            // l1 = 3X^2
            self.mod_add_to(&mut l1, &t1);
            if self.a_is_zero() {
                /* Use the faster case.  */
                /* L1 = 3X^2 */
                // do nothing
            } else if z_is_one {
                // should be mont_paramA
                // could be optimized to self.mont_param_a
                self.mod_add_to(&mut l1, &self.mont_a());
            //self.mod_add_to(&mut l1, & self.to_montgomery(self.a()));
            } else {
                // t1 = Z^4
                t1 = self.msqr(p.z(), 2);
                // t1 = a * Z^4
                t1 = self.mmult(&t1, &self.mont_a());
                //t1 = self.mmult(&t1, & self.to_montgomery(self.a()));
                // l1 = 3 X^2 + a Z^4
                self.mod_add_to(&mut l1, &t1);
            }
        }

        let mut x3: U256;
        let mut y3: U256;
        let mut z3: U256;
        /* Z3 = 2YZ */
        if z_is_one {
            z3 = self.mont_mult2(p.y());
        } else {
            // Z3 = YZ
            z3 = self.mmult(p.y(), p.z());
            // Z3 *= 2
            z3 = self.mont_mult2(&z3);
        }

        /* L2 = 4XY^2 */
        /* t2 = Y^2 */
        t2 = self.msqr(p.y(), 1);
        // t2 = 2 Y^2
        t2 = self.mont_mult2(&t2);
        // l2 =  2 XY^2
        l2 = self.mmult(&t2, p.x());
        // l2 = 4 X Y^2
        l2 = self.mont_mult2(&l2);

        /* X3 = L1^2 - 2L2 */
        /*						T1: used for 2L2. */
        x3 = self.msqr(&l1, 1);
        t1 = self.mont_mult2(&l2);
        self.mod_sub_from(&mut x3, &t1);

        /* L3 = 8Y^4 */
        /*   L3 reuse t2, t2: taken from above. */
        t2 = self.msqr(&t2, 1); // t2 = t2^2 = 4Y^4
        t2 = self.mont_mult2(&t2); // t2 *= 2, t2 = 8Y^4

        /* Y3 = L1(L2 - X3) - L3 */
        y3 = self.mod_sub(&l2, &x3);
        y3 = self.mmult(&l1, &y3);
        self.mod_sub_from(&mut y3, &t2);
        Point {
            x: x3,
            y: y3,
            z: z3,
        }
    }
    // doublez_jacob for z is mont_one
    #[inline]
    fn doublez_jacob(&self, x: &U256, y: &U256) -> Point {
        let mut t1: U256;
        let mut t2: U256;
        let mut l1: U256;
        let mut l2: U256;
        if A_IS_N3 {
            /* Use the faster case.  */
            /* L1 = 3(X - Z^2)(X + Z^2) */
            /* square is faster than multiple	*/
            // L1 = 3(X^2 - Z^4) = 3(X^2 -1)
            /*						T1: used for Z^2. */
            /*						T2: used for the right term. */
            // l1 = X^2
            l1 = self.msqr(x, 1);
            // l1 = x^2 - z^4
            self.mod_sub_from(&mut l1, self.mont_one());
            // 3(X^2 - Z^4) = 2(X^2 - Z^4) + (X^2 - Z^4)
            // t1 = 2 * l1
            t1 = self.mont_mult2(&l1);
            self.mod_add_to(&mut l1, &t1);
        } else {
            /* Standard case. */
            /* L1 = 3X^2 + aZ^4 */
            /*					T1: used for aZ^4. */
            // l1 = X^2
            l1 = self.msqr(x, 1);
            t1 = self.mont_mult2(&l1);
            // l1 = 3X^2
            self.mod_add_to(&mut l1, &t1);
            if self.a_is_zero() {
                /* Use the faster case.  */
                /* L1 = 3X^2 */
                // do nothing
            } else {
                // should be mont_paramA
                // could be optimized to self.mont_param_a
                self.mod_add_to(&mut l1, &self.mont_a());
            }
        }

        let mut x3: U256;
        let mut y3: U256;
        /* Z3 = 2YZ */
        let z3 = self.mont_mult2(y);

        /* L2 = 4XY^2 */
        /* t2 = Y^2 */
        t2 = self.msqr(y, 1);
        // t2 = 2 Y^2
        t2 = self.mont_mult2(&t2);
        // l2 =  2 XY^2
        l2 = self.mmult(&t2, x);
        // l2 = 4 X Y^2
        l2 = self.mont_mult2(&l2);

        /* X3 = L1^2 - 2L2 */
        /*						T1: used for 2L2. */
        x3 = self.msqr(&l1, 1);
        t1 = self.mont_mult2(&l2);
        self.mod_sub_from(&mut x3, &t1);

        /* L3 = 8Y^4 */
        /*   L3 reuse t2, t2: taken from above. */
        t2 = self.msqr(&t2, 1); // t2 = t2^2 = 4Y^4
        t2 = self.mont_mult2(&t2); // t2 *= 2, t2 = 8Y^4

        /* Y3 = L1(L2 - X3) - L3 */
        y3 = self.mod_sub(&l2, &x3);
        y3 = self.mmult(&l1, &y3);
        self.mod_sub_from(&mut y3, &t2);
        Point {
            x: x3,
            y: y3,
            z: z3,
        }
    }
    // add-1998-cmo-2 algorithm
    // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html
    // cost: 12M + 4S + 6add + 1*2
    //      11M + 3S + 6add + 1*2 dependent upon the first point
    fn add_jacob(&self, p: &Point, q: &Point) -> Point {
        // U1 ... l1
        // U2 ... l2
        // S1 ... l4
        // S2 ... l5
        let z1_is_one = *p.z() == *self.mont_one();
        let z2_is_one = *q.z() == *self.mont_one();

        /* u1 = x1 z2^2  */
        /* u2 = x2 z1^2  */
        // z1z1 = z1^2
        let z1z1 = if z1_is_one {
            self.mont_one().clone()
        } else {
            self.msqr(p.z(), 1)
        };
        // z2z2 = z2^2
        let z2z2 = if z2_is_one {
            self.mont_one().clone()
        } else {
            self.msqr(q.z(), 1)
        };
        // u1 = x1 z2^2
        let u1 = if z2_is_one {
            p.x().clone()
        } else {
            self.mmult(p.x(), &z2z2)
        };
        // u2 = x2 z1^2
        let u2 = if z1_is_one {
            q.x().clone()
        } else {
            self.mmult(q.x(), &z1z1)
        };

        /* h = u2 - u1 */
        let h = self.mod_sub(&u2, &u1);
        /* s1 = y1 z2^3  */
        // s1 = y1
        let s1 = if z2_is_one {
            p.y().clone()
        } else {
            // s1 = y1 z2^3
            let s1 = self.mmult(p.y(), &z2z2);
            self.mmult(&s1, q.z())
        };

        /* s2 = y2 z1^3  */
        // s2 = y2
        let s2 = if z1_is_one {
            q.y().clone()
        } else {
            // s2 = y2 z1^3
            let s2 = self.mmult(q.y(), &z1z1);
            self.mmult(&s2, p.z())
        };
        /* r = s2 - s1  */
        let r = self.mod_sub(&s2, &s1);

        if h.is_zero() {
            if r.is_zero() {
                /* P1 and P2 are the same - use duplicate function. */
                if z1_is_one {
                    return self.doublez_jacob(p.x(), p.y());
                } else {
                    return self.double_jacob(p);
                }
            }
            /* P1 is the inverse of P2.  */
            let res: Point = Default::default();
            return res;
        }
        // hh = h^2
        let hh = self.msqr(&h, 1);
        // hhh = h * hh
        let hhh = self.mmult(&hh, &h);
        // v = u1 * hh
        let v = self.mmult(&u1, &hh);

        // x3 = r^2 - hhh - 2*v
        let mut x3 = self.msqr(&r, 1);
        self.mod_sub_from(&mut x3, &hhh);
        self.mod_sub_from(&mut x3, &v);
        self.mod_sub_from(&mut x3, &v);

        // y3 = v - x3
        let mut y3 = self.mod_sub(&v, &x3);
        // y3 = r * (v - x3)
        y3 = self.mmult(&r, &y3);
        // t1 = s1 * hhh
        let t1 = self.mmult(&s1, &hhh);
        // y3 = r * (v - x3) - s1 *hhh
        self.mod_sub_from(&mut y3, &t1);

        // z3 = z1 * z2 * h
        let z3 = if z2_is_one {
            // z3 = z1 h
            if z1_is_one {
                h.clone()
            } else {
                self.mmult(p.z(), &h)
            }
        } else {
            // z3 = z1 * z2 * h
            let t1 = if z1_is_one {
                // t1 = z2
                q.z().clone()
            } else {
                // t1 = z1 * z2
                self.mmult(p.z(), q.z())
            };
            self.mmult(&t1, &h)
        };
        Point {
            x: x3,
            y: y3,
            z: z3,
        }
    }
    // addzz_jacob for pz/qz are mont_one
    #[inline]
    fn addzz_jacob(&self, px: &U256, py: &U256, qx: &U256, qy: &U256) -> Point {
        /* u1 = x1 z2^2  */
        /* u2 = x2 z1^2  */
        // z1z1 = z1^2
        // u1 = x1 z2^2
        let u1 = px.clone();
        // u2 = x2 z1^2
        let u2 = qx.clone();

        /* h = u2 - u1 */
        let h = self.mod_sub(&u2, &u1);
        /* s1 = y1 z2^3  */
        // s1 = y1
        let s1 = py.clone();

        /* s2 = y2 z1^3  */
        // s2 = y2
        let s2 = qy.clone();
        /* r = s2 - s1  */
        let r = self.mod_sub(&s2, &s1);

        if h.is_zero() {
            if r.is_zero() {
                /* P1 and P2 are the same - use duplicate function. */
                return self.doublez_jacob(qx, qy);
            }
            /* P1 is the inverse of P2.  */
            let res: Point = Default::default();
            return res;
        }
        // hh = h^2
        let hh = self.msqr(&h, 1);
        // hhh = h * hh
        let hhh = self.mmult(&hh, &h);
        // v = u1 * hh
        let v = self.mmult(&u1, &hh);

        // x3 = r^2 - hhh - 2*v
        let mut x3 = self.msqr(&r, 1);
        self.mod_sub_from(&mut x3, &hhh);
        self.mod_sub_from(&mut x3, &v);
        self.mod_sub_from(&mut x3, &v);

        // y3 = v - x3
        let mut y3 = self.mod_sub(&v, &x3);
        // y3 = r * (v - x3)
        y3 = self.mmult(&r, &y3);
        // t1 = s1 * hhh
        let t1 = self.mmult(&s1, &hhh);
        // y3 = r * (v - x3) - s1 *hhh
        self.mod_sub_from(&mut y3, &t1);

        // z3 = z1 * z2 * h
        let z3 = h;
        Point {
            x: x3,
            y: y3,
            z: z3,
        }
    }
    // addz_jacob for qz is mont_one
    #[inline]
    fn addz_jacob(&self, p: &Point, qx: &U256, qy: &U256) -> Point {
        // U1 ... l1
        // U2 ... l2
        // S1 ... l4
        // S2 ... l5
        if *p.z() == *self.mont_one() {
            return self.addzz_jacob(p.x(), p.y(), qx, qy);
        }

        /* u1 = x1 z2^2  */
        /* u2 = x2 z1^2  */
        // z1z1 = z1^2
        let z1z1 = self.msqr(p.z(), 1);
        // u1 = x1 z2^2
        let u1 = p.x().clone();
        // u2 = x2 z1^2
        let u2 = self.mmult(qx, &z1z1);

        /* h = u2 - u1 */
        let h = self.mod_sub(&u2, &u1);
        /* s1 = y1 z2^3  */
        // s1 = y1
        let s1 = p.y().clone();

        /* s2 = y2 z1^3  */
        let s2 = self.mmult(qy, &z1z1);
        // s2 = y2 z1^3
        let s2 = self.mmult(&s2, p.z());
        /* r = s2 - s1  */
        let r = self.mod_sub(&s2, &s1);

        if h.is_zero() {
            if r.is_zero() {
                /* P1 and P2 are the same - use duplicate function. */
                return self.doublez_jacob(qx, qy);
            }
            /* P1 is the inverse of P2.  */
            let res: Point = Default::default();
            return res;
        }
        // hh = h^2
        let hh = self.msqr(&h, 1);
        // hhh = h * hh
        let hhh = self.mmult(&hh, &h);
        // v = u1 * hh
        let v = self.mmult(&u1, &hh);

        // x3 = r^2 - hhh - 2*v
        let mut x3 = self.msqr(&r, 1);
        self.mod_sub_from(&mut x3, &hhh);
        self.mod_sub_from(&mut x3, &v);
        self.mod_sub_from(&mut x3, &v);

        // y3 = v - x3
        let mut y3 = self.mod_sub(&v, &x3);
        // y3 = r * (v - x3)
        y3 = self.mmult(&r, &y3);
        // t1 = s1 * hhh
        let t1 = self.mmult(&s1, &hhh);
        // y3 = r * (v - x3) - s1 *hhh
        self.mod_sub_from(&mut y3, &t1);

        // z3 = z1 * z2 * h
        let z3 = self.mmult(p.z(), &h);
        Point {
            x: x3,
            y: y3,
            z: z3,
        }
    }
    fn point_on_curve(&self, p: &Point) -> bool {
        // y^2
        let y2 = self.msqr(p.y(), 1);
        let y2 = self.from_montgomery(&y2);
        let x2 = self.msqr(p.x(), 1);
        // x2a = x^2 + a
        let x2a = self.mod_add(&x2, self.mont_a());
        let tt = self.mmult(&x2a, p.x());
        // tt = x^3 + ax
        let tt = self.from_montgomery(&tt);
        // tt = x^3 + ax + b
        let tt = self.mod_add(&tt, self.b());
        y2 == tt
    }
    // return point(x, y) from jacobian affined point(x, y, z)
    // in forms of Montgomery
    // called internal
    #[inline]
    fn apply_z_mont(&self, p: &Point) -> (U256, U256) {
        let z = self.from_montgomery(p.z());
        // ziv = p.z^-1
        let ziv = z.mod_inverse(self.p());
        let z = self.to_montgomery(&ziv);
        // t = p.z^-2
        let t = self.msqr(&z, 1);
        let x = self.mmult(p.x(), &t);
        // t = p.z^-3
        let t = self.mmult(&t, &z);
        let y = self.mmult(p.y(), &t);
        (x, y)
    }
    fn to_montgomery(&self, x: &U256) -> U256 {
        self.p_mont().to_montgomery(x)
    }
    fn from_montgomery(&self, x: &U256) -> U256 {
        self.p_mont().reduction(x)
    }
    #[allow(non_snake_case)]
    fn to_montgomeryN(&self, x: &U256) -> U256 {
        self.n_mont().to_montgomery(x)
    }
    #[allow(non_snake_case)]
    fn from_montgomeryN(&self, x: &U256) -> U256 {
        self.n_mont().reduction(x)
    }
    fn pre_compute(&self, pre_comp: &mut [Point; wSize], p: &Point) {
        pre_comp[0] = p.clone();
        pre_comp[1] = self.doublez_jacob(p.x(), p.y());
        for i in 2..wSize {
            if (i & 1) == 0 {
                pre_comp[i] = self.addz_jacob(&pre_comp[i - 1], p.x(), p.y());
            } else {
                pre_comp[i] = self.double_jacob(&pre_comp[i >> 1]);
            }
        }
    }
    fn scalar_mult(&self, px: &U256, py: &U256, scalar: &U256) -> (U256, U256) {
        let pt = self.scalar_mult_internal(px, py, scalar);
        let (x, y) = self.apply_z_mont(&pt);
        (self.from_montgomery(&x), self.from_montgomery(&y))
    }
    fn scalar_base_mult(&self, scalar: &U256) -> (U256, U256) {
        let pt = self.base_mult(scalar);
        let (x, y) = self.apply_z_mont(&pt);
        (self.from_montgomery(&x), self.from_montgomery(&y))
    }
    fn y_recover(&self, x: &U256, is_odd: bool) -> Option<U256> {
        let t1 = self.to_montgomery(x);
        // t2 = x^2 + a
        let t2 = self.msqr(&t1, 1);
        let t2 = t2.mod_add(self.mont_a(), self.p());
        // t2 = x^3 + ax
        let t2 = self.mmult(&t2, &t1);
        // t1 = t2 + b = x^3 + ax +b
        let mont_b = self.to_montgomery(self.b());
        let t1 = t2.mod_add(&mont_b, self.p());
        // need mod_sqrt
        // y^2 = x^3 + ax + b
        if let Some(yp) = self.p_mont().sqrt(&t1) {
            let mut y = self.from_montgomery(&yp);
            // odd for negative bignum
            if is_odd ^ y.is_odd() {
                (y, _) = self.p().sub(&y);
            }
            Some(y)
        } else {
            return None;
        }
    }
    fn base_mult(&self, scalar: &U256) -> Point {
        let mut skip: bool = true;
        let mut idx: i32 = -1;
        let mut q: Point = Default::default();
        for lvl in 0..maxBaseNAF as u64 {
            let bits = scalar.get_bits::<{ BaseW + 1 }>(idx);
            let (digit, sign) = recode_scalar_bits::<BaseW>(bits);
            idx += BaseW as i32;
            if digit == 0 {
                continue;
            }
            let digit = digit - 1;
            let (tx, ty) = self.select_base_point(digit, lvl, sign as u64);
            if !skip {
                q = self.addz_jacob(&q, &tx, &ty);
            } else {
                q = Point {
                    x: tx,
                    y: ty,
                    z: self.mont_one().clone(),
                };
                skip = false;
            }
        }
        q
    }
    fn scalar_mult_internal(&self, px: &U256, py: &U256, scalar: &U256) -> Point {
        assert!(!scalar.is_zero());
        let res = MaybeUninit::<[Point; wSize]>::uninit();
        let mut pre_comps = unsafe { res.assume_init() };
        let p = self.to_affined(px, py);
        self.pre_compute(&mut pre_comps, &p);
        let mut nbits: u64 = 256; // 4 * 64
        let off = 256 % W;
        let off = if off == 0 { W } else { off };
        nbits -= 1;
        let mut idx: i32 = nbits as i32 - off as i32;
        let mut skip: bool = true;
        let mut q: Point = Default::default();
        {
            let bits = scalar.get_bits::<{ W + 1 }>(idx);
            let (digit, _) = recode_scalar_bits::<W>(bits);
            if digit != 0 {
                let digit = digit as usize - 1;
                q = pre_comps[digit].clone();
                skip = false;
            }
        }
        while idx >= 0 {
            idx -= W as i32;
            if !skip {
                for _j in 0..W {
                    q = self.double_jacob(&q);
                }
            }
            let bits = scalar.get_bits::<{ W + 1 }>(idx);
            let (digit, sign) = recode_scalar_bits::<W>(bits);
            if digit == 0 {
                continue;
            }
            let digit = digit as usize - 1;
            let tmp = pre_comps[digit].select_neg(self.p(), sign as u64);
            if !skip {
                q = self.add_jacob(&q, &tmp);
            } else {
                q = tmp.clone();
                skip = false;
            }
        }
        q
    }
    fn cmult(&self, px: &U256, py: &U256, scalar: &U256, g_scalar: &U256) -> Point {
        let mut res: Point = Default::default();
        if g_scalar.is_zero() {
            return res;
        }
        res = self.base_mult(g_scalar);
        if !scalar.is_zero() {
            let tmp = self.scalar_mult_internal(px, py, scalar);
            res = self.add_jacob(&res, &tmp);
        }
        res
    }
}

impl<const K0: u64, const NK0: u64, const A_IS_N3: bool> fmt::Display
    for dyn CurveSW<K0, NK0, A_IS_N3>
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "CurveSW ({}):\n\tp->{}\n\tn->{}\n\ta->{}\n\tb->{}\n\tgx->{}\n\tgy->{}",
            self.name(),
            self.p(),
            self.n(),
            self.a(),
            self.b(),
            self.gx(),
            self.gy()
        )
    }
}

fn g_precompute<const K0: u64, const NK0: u64, const A_IS_N3: bool>(
    curve: &dyn CurveSW<K0, NK0, A_IS_N3>,
    x: &U256,
    y: &U256,
) -> [GwNAF; maxBaseNAF] {
    debug_assert!(maxBaseNAF == 43, "only 256 bits curve supported");
    debug_assert!(
        (4 * 64 % BaseW) != 0,
        "curve Bits MUUST not multiples of BaseW"
    );
    let g_pres = MaybeUninit::<[GwNAF; maxBaseNAF]>::uninit();
    let mut g_precomps = unsafe { g_pres.assume_init() };
    let gx = curve.to_montgomery(x);
    let gy = curve.to_montgomery(y);
    let gz = curve.mont_one();

    let mut t2 = Point::new(&gx, &gy, gz);

    for j in 0..wBaseSize {
        let mut t1 = t2.clone();
        for i in 0..maxBaseNAF {
            // The window size is 6 so we need to double 6 times.
            // baseW is the window size
            if i != 0 {
                t1 = curve.doublez_jacob(t1.x(), t1.y());
                for _k in 1..BaseW {
                    t1 = curve.double_jacob(&t1);
                }
            }
            // Convert the point to affine form. (Its values are
            // still in Montgomery form however.)
            let (tx, ty) = curve.apply_z_mont(&t1);
            t1 = Point {
                x: tx.clone(),
                y: ty.clone(),
                z: gz.clone(),
            };

            // Update the table entry
            g_precomps[i][j] = (tx, ty);
        }
        if j == 0 {
            t2 = curve.doublez_jacob(&gx, &gy);
        } else {
            t2 = curve.addz_jacob(&t2, &gx, &gy);
        }
    }
    g_precomps
}

// CurveN3 for a == p - 3
pub struct CurveN3<const K0: u64, const NK0: u64> {
    p: U256,  // p prime, the order of the underlying field
    n: U256,  // n prime, the order of the base point
    a: U256,  // a, the constant of the curve equation
    b: U256,  // b, the constant of the curve equation
    gx: U256, // x coordinate of the base point
    gy: U256, // y coordinate of the base point
    mont_a: U256,
    mont_one: U256,
    p_mont: Mont<K0>,
    n_mont: Mont<NK0>,
    name: String, // the canonical name of the curve
    g_precomps: [GwNAF; maxBaseNAF],
}

pub type TypeSM2 = CurveN3<SM2_P_K0, SM2_N_K0>;

impl<const K0: u64, const NK0: u64> fmt::Display for CurveN3<K0, NK0> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "CurveN3 ({}):\n\tp->{}\n\tn->{}\n\tb->{}\n\tgx->{}\n\tgy->{}",
            self.name, self.p, self.n, self.b, self.gx, self.gy
        )
    }
}

impl<const K0: u64, const NK0: u64> CurveSW<K0, NK0, true> for CurveN3<K0, NK0> {
    fn p(&self) -> &U256 {
        &self.p
    }
    fn n(&self) -> &U256 {
        &self.n
    }
    fn a(&self) -> &U256 {
        &self.a
    }
    fn b(&self) -> &U256 {
        &self.b
    }
    fn gx(&self) -> &U256 {
        &self.gx
    }
    fn gy(&self) -> &U256 {
        &self.gy
    }
    fn name(&self) -> &str {
        &self.name
    }
    #[inline]
    fn p_mont(&self) -> &dyn Montgomery<K0> {
        &self.p_mont
    }
    #[inline]
    fn n_mont(&self) -> &dyn Montgomery<NK0> {
        &self.n_mont
    }
    #[inline]
    fn mont_a(&self) -> &U256 {
        &self.mont_a
    }
    // montgomery one GF(p)
    #[inline]
    fn mont_one(&self) -> &U256 {
        &self.mont_one
    }
    fn select_base_point(&self, digit: u32, lvl: u64, sign: u64) -> (U256, U256) {
        let pt = &self.g_precomps[lvl as usize][digit as usize];
        let (y, _) = self.p().sub(&pt.1);
        let y = y.move_cond(&pt.1, sign);
        (pt.0.clone(), y)
    }
    fn select_base_NAF(&self, lvl: u64) -> &GwNAF {
        &self.g_precomps[lvl as usize]
    }
}

impl<const K0: u64, const NK0: u64> CurveN3<K0, NK0> {
    pub fn new(p: &U256, n: &U256, b: &U256, gx: &U256, gy: &U256, name: &str) -> CurveN3<K0, NK0> {
        let mut a = p.clone();
        // a = p - 3
        a.usub(3);
        let p_mont: Mont<K0> = Mont::new(p);
        let n_mont: Mont<NK0> = Mont::new(n);
        let mont_one = p_mont.mont_one();
        let mont_a = p.mod_sub(&mont_one, p); // p - 1
        let mont_a = mont_a.mod_sub(&mont_one, p); // p - 2
        let mont_a = mont_a.mod_sub(&mont_one, p); // p - 3
        let g_pres = MaybeUninit::<[GwNAF; maxBaseNAF]>::uninit();
        let g_precomps = unsafe { g_pres.assume_init() };
        let mut res = CurveN3::<K0, NK0> {
            p: p.clone(),
            n: n.clone(),
            a,
            b: b.clone(),
            gx: gx.clone(),
            gy: gy.clone(),
            mont_a,
            mont_one,
            p_mont,
            n_mont,
            name: name.to_string(),
            g_precomps,
        };
        res.init_table();
        res
    }
    fn init_table(&mut self) {
        self.g_precomps = g_precompute::<K0, NK0, true>(self, self.gx(), self.gy());
    }
}

use crate::ecc_const::{SM2_B, SM2_GX, SM2_GY};

lazy_static! {
    static ref SM2_PRECOMPS: [GwNAF; maxBaseNAF] = {
        let res = CurveSM2::new("sm2p");
        g_precompute::<SM2_P_K0, SM2_N_K0, true>(&res, &SM2_GX, &SM2_GY)
    };
}

// The underlying field. SM2-P256 operates over GF(2^256-2^224-2^96+2^64-1)
pub struct CurveSM2 {
    a: U256,
    mont_a: U256,
    mont_one: U256,
    p_minus_n: U256,
    p_mont: MontSM2p,
    n_mont: Mont<SM2_N_K0>,
    name: String, // the canonical name of the curve
}

impl fmt::Display for CurveSM2 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "CurveSM2 ({}):\n\tp->{}\n\tn->{}\n\tb->{}\n\tgx->{}\n\tgy->{}",
            self.name,
            self.p(),
            self.n(),
            self.b(),
            self.gx(),
            self.gy()
        )
    }
}

impl CurveSW<SM2_P_K0, SM2_N_K0, true> for CurveSM2 {
    fn p(&self) -> &U256 {
        &SM2_P
    }
    fn n(&self) -> &U256 {
        &SM2_N
    }
    fn a(&self) -> &U256 {
        &self.a
    }
    fn b(&self) -> &U256 {
        &SM2_B
    }
    fn gx(&self) -> &U256 {
        &SM2_GX
    }
    fn gy(&self) -> &U256 {
        &SM2_GY
    }
    fn name(&self) -> &str {
        &self.name
    }
    fn p_mont(&self) -> &dyn Montgomery<SM2_P_K0> {
        &self.p_mont
    }
    fn n_mont(&self) -> &dyn Montgomery<SM2_N_K0> {
        &self.n_mont
    }
    fn mont_a(&self) -> &U256 {
        &self.mont_a
    }
    #[inline]
    fn p_minus_n(&self) -> U256 {
        self.p_minus_n.clone()
    }
    // mmult -- p_mont mult
    fn mmult(&self, x: &U256, y: &U256) -> U256 {
        self.p_mont.mult(x, y)
    }
    // msqr -- pmont sqr
    fn msqr(&self, x: &U256, n: u32) -> U256 {
        self.p_mont.sqr(x, n)
    }
    // montgomery one GF(p)
    fn mont_one(&self) -> &U256 {
        &self.mont_one
    }
    fn to_montgomery(&self, x: &U256) -> U256 {
        self.p_mont.to_montgomery(x)
    }
    fn from_montgomery(&self, x: &U256) -> U256 {
        self.p_mont.reduction(x)
    }
    fn select_base_point(&self, digit: u32, lvl: u64, sign: u64) -> (U256, U256) {
        let pt = &SM2_PRECOMPS[lvl as usize][digit as usize];
        let (y, _) = self.p().sub(&pt.1);
        let y = y.move_cond(&pt.1, sign);
        (pt.0.clone(), y)
    }
    fn select_base_NAF(&self, lvl: u64) -> &GwNAF {
        &SM2_PRECOMPS[lvl as usize]
    }
}

impl CurveSM2 {
    pub fn new(name: &str) -> CurveSM2 {
        let mut a = SM2_P.clone();
        // a = p - 3
        a.usub(3);
        let p_mont = MontSM2p::new();
        let n_mont: Mont<SM2_N_K0> = Mont::new(&SM2_N);
        let mont_one = p_mont.mont_one();
        let mont_a = SM2_P.mod_sub(&mont_one, &SM2_P); // p - 1
        let mont_a = mont_a.mod_sub(&mont_one, &SM2_P); // p - 2
        let mont_a = mont_a.mod_sub(&mont_one, &SM2_P); // p - 3
        let (p_minus_n, _) = SM2_P.sub(&SM2_N);
        //let g_pres = MaybeUninit::<[GwNAF; maxBaseNAF]>::uninit();
        //let g_precomps = unsafe { g_pres.assume_init() };
        let res = CurveSM2 {
            a,
            mont_a,
            mont_one,
            p_minus_n,
            p_mont,
            n_mont,
            name: name.to_string(),
        };
        res
    }
}

/*
 * This function looks at w+1 scalar bits (5 current, 1 adjacent less
 * significant bit), and recodes them into a signed digit for use in fast point
 * multiplication: the use of signed rather than unsigned digits means that
 * fewer points need to be precomputed, given that point inversion is easy
 * (a precomputed point dP makes -dP available as well).
 *
 * BACKGROUND:
 *
 * Signed digits for multiplication were introduced by Booth ("A signed binary
 * multiplication technique", Quart. Journ. Mech. and Applied Math., vol. IV,
 * pt. 2 (1951), pp. 236-240), in that case for multiplication of integers.
 * Booth's original encoding did not generally improve the density of nonzero
 * digits over the binary representation, and was merely meant to simplify the
 * handling of signed factors given in two's complement; but it has since been
 * shown to be the basis of various signed-digit representations that do have
 * further advantages, including the wNAF, using the following general approach:
 *
 * (1) Given a binary representation
 *
 *       b_k  ...  b_2  b_1  b_0,
 *
 *     of a nonnegative integer (b_k in {0, 1}), rewrite it in digits 0, 1, -1
 *     by using bit-wise subtraction as follows:
 *
 *        b_k b_(k-1)  ...  b_2  b_1  b_0
 *      -     b_k      ...  b_3  b_2  b_1  b_0
 *       -------------------------------------
 *        s_k b_(k-1)  ...  s_3  s_2  s_1  s_0
 *
 *     A left-shift followed by subtraction of the original value yields a new
 *     representation of the same value, using signed bits s_i = b_(i+1) - b_i.
 *     This representation from Booth's paper has since appeared in the
 *     literature under a variety of different names including "reversed binary
 *     form", "alternating greedy expansion", "mutual opposite form", and
 *     "sign-alternating {+-1}-representation".
 *
 *     An interesting property is that among the nonzero bits, values 1 and -1
 *     strictly alternate.
 *
 * (2) Various window schemes can be applied to the Booth representation of
 *     integers: for example, right-to-left sliding windows yield the wNAF
 *     (a signed-digit encoding independently discovered by various researchers
 *     in the 1990s), and left-to-right sliding windows yield a left-to-right
 *     equivalent of the wNAF (independently discovered by various researchers
 *     around 2004).
 *
 * To prevent leaking information through side channels in point multiplication,
 * we need to recode the given integer into a regular pattern: sliding windows
 * as in wNAFs won't do, we need their fixed-window equivalent -- which is a few
 * decades older: we'll be using the so-called "modified Booth encoding" due to
 * MacSorley ("High-speed arithmetic in binary computers", Proc. IRE, vol. 49
 * (1961), pp. 67-91), in a radix-2^5 setting.  That is, we always combine five
 * signed bits into a signed digit:
 *
 *       s_(4j + 4) s_(4j + 3) s_(4j + 2) s_(4j + 1) s_(4j)
 * The sign-alternating property implies that the resulting digit values are
 * integers from -16 to 16.
 *
 * Of course, we don't actually need to compute the signed digits s_i as an
 * intermediate step (that's just a nice way to see how this scheme relates
 * to the wNAF): a direct computation obtains the recoded digit from the
 * six bits b_(4j + 4) ... b_(4j - 1).
 *
 * This function takes those five bits as an integer (0 .. 63), writing the
 * recoded digit to *sign (0 for positive, 1 for negative) and *digit (absolute
 * value, in the range 0 .. 8).  Note that this integer essentially provides the
 * input bits "shifted to the left" by one position: for example, the input to
 * compute the least significant recoded digit, given that there's no bit b_-1,
 * has to be b_4 b_3 b_2 b_1 b_0 0.
 *
 */
// boothW
#[inline]
fn recode_scalar_bits<const W: u64>(inp: u32) -> (u32, i32) {
    assert!(W > 2 && W <= 8, "w of wNAF between 3 .. 8");
    let s: i32;
    let mut d: i32;

    s = !((inp as i32 >> W) - 1); /* sets all bits to MSB(in), 'in' seen as
                                   * W+1 bit value */
    d = (1 << (W + 1)) - inp as i32 - 1;
    d = (d & s) | (inp as i32 & !s);
    d = (d >> 1) + (d & 1);

    (d as u32, s & 1)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ecc_const::{SM2_B, SM2_GX, SM2_GY, SM2_N_RR, SM2_P_RR};
    use crate::ecc_const::{SM2_N, SM2_N_K0, SM2_P, SM2_P_K0};
    use crate::test_data::tests::*;
    use log::{info, warn};
    use measure::Measure;
    use simple_logger::SimpleLogger;

    #[test]
    fn test_recode_bits() {
        assert_eq!(recode_scalar_bits::<4>(5), (3, 0));
        assert_eq!(recode_scalar_bits::<4>(8), (4, 0));
        assert_eq!(recode_scalar_bits::<4>(25), (3, 1));
        assert_eq!(recode_scalar_bits::<4>(19), (6, 1));
    }

    #[test]
    #[allow(non_snake_case)]
    fn test_CurveN3() {
        if let Err(s) = SimpleLogger::new().init() {
            warn!("SimpleLogger init: {}", s);
        }
        let curve: TypeSM2 = TypeSM2::new(&SM2_P, &SM2_N, &SM2_B, &SM2_GX, &SM2_GY, "sm2-n3");
        info!("dump curve:\n{}", &curve);
        // test p_mont
        let dx1_r1 = curve.from_montgomery(&dx1_m);
        assert!(dx1_r1 == dx1);
        let dx1_r1 = curve.p_mont.reduction(&dx1_m);
        assert!(dx1_r1 == dx1);
        assert!(curve.p_mont.rr() == SM2_P_RR);
        assert!(curve.n_mont().rr() == SM2_N_RR);
        info!("test add_jacobian");
        let p = curve.to_affined(&dx1, &dy1);
        let q = curve.to_affined(&dx2, &dy2);
        let pt = curve.add_jacob(&p, &q);
        let x3 = curve.from_montgomery(pt.x());
        let y3 = curve.from_montgomery(pt.y());
        let z3 = curve.from_montgomery(pt.z());
        assert!(x3 == dx3);
        assert!(y3 == dy3);
        assert!(z3 == dz3);
        info!("test addz_jacobian");
        let pt = curve.addz_jacob(&p, q.x(), q.y());
        let x3 = curve.from_montgomery(pt.x());
        let y3 = curve.from_montgomery(pt.y());
        let z3 = curve.from_montgomery(pt.z());
        assert!(x3 == dx3);
        assert!(y3 == dy3);
        assert!(z3 == dz3);
        info!("test double_jacobian");
        let pt = curve.double_jacob(&p);
        let x3 = curve.from_montgomery(pt.x());
        let y3 = curve.from_montgomery(pt.y());
        let z3 = curve.from_montgomery(pt.z());
        assert!(x3 == x1x1);
        assert!(y3 == y1y1);
        assert!(z3 == z1z1);
        info!("test doublez_jacobian");
        let pt = curve.doublez_jacob(p.x(), p.y());
        let x3 = curve.from_montgomery(pt.x());
        let y3 = curve.from_montgomery(pt.y());
        let z3 = curve.from_montgomery(pt.z());
        assert!(x3 == x1x1);
        assert!(y3 == y1y1);
        assert!(z3 == z1z1);
    }

    #[test]
    #[allow(non_snake_case)]
    fn test_CurveSM2() {
        if let Err(s) = SimpleLogger::new().init() {
            warn!("SimpleLogger init: {}", s);
        }
        let curve = CurveSM2::new("sm2p");
        info!("dump curve:\n{}", curve);
        // test p_mont
        let dx1_r1 = curve.from_montgomery(&dx1_m);
        assert!(dx1_r1 == dx1);
        let dx1_r1 = curve.p_mont.reduction(&dx1_m);
        assert!(dx1_r1 == dx1);
        assert!(curve.p_mont.rr() == SM2_P_RR);
        assert!(curve.n_mont().rr() == SM2_N_RR);
        let curve_r = &curve as &dyn CurveSW<SM2_P_K0, SM2_N_K0, true>;
        info!("dump curve:\n{}", curve_r);
        info!("test add_jacobian");
        let p = curve.to_affined(&dx1, &dy1);
        let q = curve.to_affined(&dx2, &dy2);
        let pt = curve.add_jacob(&p, &q);
        let x3 = curve.from_montgomery(pt.x());
        let y3 = curve.from_montgomery(pt.y());
        let z3 = curve.from_montgomery(pt.z());
        assert!(x3 == dx3);
        assert!(y3 == dy3);
        assert!(z3 == dz3);
        info!("test double_jacobian");
        let pt = curve.double_jacob(&p);
        let x3 = curve.from_montgomery(pt.x());
        let y3 = curve.from_montgomery(pt.y());
        let z3 = curve.from_montgomery(pt.z());
        assert!(x3 == x1x1);
        assert!(y3 == y1y1);
        assert!(z3 == z1z1);
    }

    #[test]
    fn test_pre_compute() {
        use std::mem::MaybeUninit;
        if let Err(s) = SimpleLogger::new().init() {
            warn!("SimpleLogger init: {}", s);
        }
        let mut measure = Measure::start("g_pre_compute bench");
        let curve = CurveSM2::new("sm2p");
        measure.stop();
        let us_ops = measure.as_us();
        warn!("g_pre_compute cost {} us", us_ops);
        let res = MaybeUninit::<[Point; wSize]>::uninit();
        let mut pre_comps = unsafe { res.assume_init() };
        //let mut pre_comps: [Point;wSize] = Default::default();
        let p = curve.to_affined(&SM2_GX, &SM2_GY);
        let mut measure = Measure::start("pre_compute bench");
        curve.pre_compute(&mut pre_comps, &p);
        measure.stop();
        let ns_ops = measure.as_ns();
        warn!("pre_compute cost {} ns for wSize({}) ", ns_ops, wSize);
        #[cfg(feature = "tsc")]
        {
            let ns_ops = measure.as_ticks();
            warn!("pre_compute cost {} ticks for wSize({})", ns_ops, wSize);
        }
        assert!(pre_comps[0] == p);
        let tmp = curve.double_jacob(&p);
        assert!(pre_comps[1] == tmp);
        let tmp1 = curve.add_jacob(&tmp, &p);
        assert!(tmp1 == pre_comps[2]);
        let tmp = curve.double_jacob(&tmp);
        assert!(pre_comps[3] == tmp);
        let tmp = curve.double_jacob(&tmp);
        assert!(pre_comps[7] == tmp);
        let tmp1 = curve.add_jacob(&tmp, &p);
        assert!(pre_comps[8] == tmp1);
        let tmp = curve.double_jacob(&tmp);
        assert!(pre_comps[15] == tmp);
    }

    #[test]
    fn test_gpre_compute() {
        if let Err(s) = SimpleLogger::new().init() {
            warn!("SimpleLogger init: {}", s);
        }
        let curve = CurveSM2::new("sm2p");
        //let mut pre_comps: [Point;wSize] = Default::default();
        let p = curve.to_affined(&SM2_GX, &SM2_GY);
        let pre_comps = curve.select_base_NAF(0);
        assert!(pre_comps[0].0 == *p.x());
        assert!(pre_comps[0].1 == *p.y());
        let tmp = curve.double_jacob(&p);
        let (tx, ty) = curve.apply_z_mont(&tmp);
        assert!(pre_comps[1].0 == tx);
        assert!(pre_comps[1].1 == ty);
        let tmp1 = curve.add_jacob(&tmp, &p);
        let (tx, ty) = curve.apply_z_mont(&tmp1);
        assert!(pre_comps[2].0 == tx);
        assert!(pre_comps[2].1 == ty);
        let tmp = curve.double_jacob(&tmp);
        let (tx, ty) = curve.apply_z_mont(&tmp);
        assert!(pre_comps[3].0 == tx);
        assert!(pre_comps[3].1 == ty);
        let tmp = curve.double_jacob(&tmp);
        let (tx, ty) = curve.apply_z_mont(&tmp);
        assert!(pre_comps[7].0 == tx);
        assert!(pre_comps[7].1 == ty);
        let tmp1 = curve.add_jacob(&tmp, &p);
        let (tx, ty) = curve.apply_z_mont(&tmp1);
        assert!(pre_comps[8].0 == tx);
        assert!(pre_comps[8].1 == ty);
        let tmp = curve.double_jacob(&tmp);
        let (tx, ty) = curve.apply_z_mont(&tmp);
        assert!(pre_comps[15].0 == tx);
        assert!(pre_comps[15].1 == ty);
        let tmp1 = curve.add_jacob(&tmp, &tmp1);
        let (tx, ty) = curve.apply_z_mont(&tmp1);
        assert!(pre_comps[24].0 == tx);
        assert!(pre_comps[24].1 == ty);
        let pre_comps1 = curve.select_base_NAF(1);
        let (tx, ty) = curve.apply_z_mont(&tmp);
        let mut tmp = curve.doublez_jacob(&tx, &ty);
        let (tx, ty) = curve.apply_z_mont(&tmp1);
        let mut tmp1 = curve.doublez_jacob(&tx, &ty);
        for _i in 1..6 {
            tmp = curve.double_jacob(&tmp);
            tmp1 = curve.double_jacob(&tmp1);
        }
        let (tx, ty) = curve.apply_z_mont(&tmp);
        assert!(pre_comps1[15].0 == tx);
        assert!(pre_comps1[15].1 == ty);
        let (tx, ty) = curve.apply_z_mont(&tmp1);
        assert!(pre_comps1[24].0 == tx);
        assert!(pre_comps1[24].1 == ty);
        let (tx, ty) = curve.select_base_point(24, 1, 0);
        assert!(pre_comps1[24].0 == tx);
        assert!(pre_comps1[24].1 == ty);
    }

    #[test]
    fn test_scalar_mult() {
        if let Err(s) = SimpleLogger::new().init() {
            warn!("SimpleLogger init: {}", s);
        }
        let curve = CurveSM2::new("sm2p");
        let (d1gx_r, d1gy_r) = curve.scalar_mult(&SM2_GX, &SM2_GY, &d1d);
        assert!(d1gx_r == d1Gx);
        assert!(d1gy_r == d1Gy);
        let (d2gx_r, d2gy_r) = curve.scalar_mult(&SM2_GX, &SM2_GY, &d2d);
        assert!(d2gx_r == d2Gx);
        assert!(d2gy_r == d2Gy);
    }

    #[test]
    fn test_scalar_base_mult() {
        if let Err(s) = SimpleLogger::new().init() {
            warn!("SimpleLogger init: {}", s);
        }
        let curve = CurveSM2::new("sm2p");
        let (d1gx_r, d1gy_r) = curve.scalar_base_mult(&d1d);
        if d1gx_r != d1Gx {
            println!("expect: {}\ngot: {}", &d1Gx, &d1gx_r);
        }
        assert!(d1gx_r == d1Gx);
        assert!(d1gy_r == d1Gy);
        let (d2gx_r, d2gy_r) = curve.scalar_base_mult(&d2d);
        assert!(d2gx_r == d2Gx);
        assert!(d2gy_r == d2Gy);
    }

    #[test]
    fn test_pointy_recover() {
        let curve = CurveSM2::new("sm2p");
        if let Some(res) = curve.y_recover(&d1Gx, d1Gy.is_odd()) {
            assert!(res == d1Gy);
        } else {
            assert!(false, "can't recover1 pointy");
        }
        if let Some(res) = curve.y_recover(&d2Gx, d2Gy.is_odd()) {
            assert!(res == d2Gy);
        } else {
            assert!(false, "can't recover2 pointy");
        }
    }

    #[test]
    #[ignore]
    #[allow(non_snake_case)]
    fn bench_CurveN3() {
        let curve: CurveN3<SM2_P_K0, SM2_N_K0> =
            CurveN3::new(&SM2_P, &SM2_N, &SM2_B, &SM2_GX, &SM2_GY, "sm2-n3");
        let p = curve.to_affined(&dx1, &dy1);
        let q = curve.to_affined(&dx2, &dy2);
        const N: u32 = 1000_000;
        let mut measure = Measure::start("sm2 add_jacob bench");
        for _i in 0..N {
            let _pt = curve.add_jacob(&p, &q);
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2 add_jacob cost {} ns per Op", ns_ops);
        let mut measure = Measure::start("sm2 addz_jacob bench");
        for _i in 0..N {
            let _pt = curve.addz_jacob(&p, q.x(), q.y());
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2 addz_jacob cost {} ns per Op", ns_ops);
        let mut measure = Measure::start("sm2 double_jacob bench");
        for _i in 0..N {
            let _pt = curve.double_jacob(&p);
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2 double_jacob cost {} ns per Op", ns_ops);
        let mut measure = Measure::start("sm2 doublez_jacob bench");
        for _i in 0..N {
            let _pt = curve.doublez_jacob(p.x(), p.y());
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2 doublez_jacob cost {} ns per Op", ns_ops);
    }

    #[test]
    #[ignore]
    #[allow(non_snake_case)]
    fn bench_CurveSM2_g_precomps() {
        let curve = CurveSM2::new("sm2p");
        const N: u32 = 100;
        let mut measure = Measure::start("sm2p g_precompute bench");
        for _i in 0..N {
            let _ = g_precompute::<SM2_P_K0, SM2_N_K0, true>(&curve, &SM2_GX, &SM2_GY);
        }
        measure.stop();
        let ns_ops = measure.as_us() / (N as u64);
        println!("sm2p g_precompute cost {} us per Op", ns_ops);
    }

    #[test]
    #[ignore]
    #[allow(non_snake_case)]
    fn bench_CurveSM2() {
        let curve = CurveSM2::new("sm2p");
        let p = curve.to_affined(&dx1, &dy1);
        let q = curve.to_affined(&dx2, &dy2);
        const N: u32 = 1000_000;
        let mut measure = Measure::start("sm2p add_jacob bench");
        for _i in 0..N {
            let _pt = curve.add_jacob(&p, &q);
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2p add_jacob cost {} ns per Op", ns_ops);
        let mut measure = Measure::start("sm2p addz_jacob bench");
        for _i in 0..N {
            let _pt = curve.addz_jacob(&p, q.x(), q.y());
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2p addz_jacob cost {} ns per Op", ns_ops);
        let mut measure = Measure::start("sm2p double_jacob bench");
        for _i in 0..N {
            let _pt = curve.double_jacob(&p);
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2p double_jacob cost {} ns per Op", ns_ops);
        let mut measure = Measure::start("sm2p doublez_jacob bench");
        for _i in 0..N {
            let _pt = curve.doublez_jacob(p.x(), p.y());
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2p doublez_jacob cost {} ns per Op", ns_ops);
    }

    #[test]
    #[ignore]
    #[allow(non_snake_case)]
    fn bench_scalar_mult() {
        let curve = CurveSM2::new("sm2p");
        const N: u32 = 10_000;
        let mut measure = Measure::start("sm2p scalar_mult bench");
        for _i in 0..N {
            let (_rx, _ry) = curve.scalar_mult(&SM2_GX, &SM2_GY, &d1d);
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2p scalar_mult cost {} ns per Op", ns_ops);
        #[cfg(feature = "tsc")]
        {
            let ns_ops = measure.as_ticks() / (N as u64);
            println!("sm2p scalar_mult cost {} ticks per Op", ns_ops);
        }
    }

    #[test]
    #[ignore]
    #[allow(non_snake_case)]
    fn bench_scalar_base_mult() {
        let curve = CurveSM2::new("sm2p");
        const N: u32 = 100_000;
        let mut measure = Measure::start("sm2p scalar_base_mult bench");
        for _i in 0..N {
            let (_rx, _ry) = curve.scalar_base_mult(&d1d);
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2p scalar_base_mult cost {} ns per Op", ns_ops);
        #[cfg(feature = "tsc")]
        {
            let ns_ops = measure.as_ticks() / (N as u64);
            println!("sm2p scalar_base_mult cost {} ticks per Op", ns_ops);
        }
    }

    #[test]
    #[ignore]
    #[allow(non_snake_case)]
    fn bench_pointy_recover() {
        let curve = CurveSM2::new("sm2p");
        const N: u32 = 100_000;
        let mut measure = Measure::start("sm2p pointY_recover bench");
        for _i in 0..N {
            let _res = curve.y_recover(&d1Gx, d1Gy.is_odd());
        }
        measure.stop();
        let ns_ops = measure.as_ns() / (N as u64);
        println!("sm2p pointY_recover cost {} ns per Op", ns_ops);
        #[cfg(feature = "tsc")]
        {
            let ns_ops = measure.as_ticks() / (N as u64);
            println!("sm2p pointY_recover cost {} ticks per Op", ns_ops);
        }
    }
}
